#include "AABBox.h"
#include "Ray.h"
#include "ContactData.h"
#include <assert.h>

bool AABBox::Hit(const Ray& ray, float& tmin, ContactData& data) const
{
    //splt the ray into 6 floats here,
    float ox = ray.m_origin.m_x; //origin
    float oy = ray.m_origin.m_y;
    float oz = ray.m_origin.m_z;

    float dx = ray.m_dir.m_x; // direction
    float dy = ray.m_dir.m_y;
    float dz = ray.m_dir.m_z;

    //declare the main values 
    float tx_min, ty_min, tz_min;
    float tx_max, ty_max, tz_max;

    float a = 1.0f / (dx != 0 ? dx : 1.0f);
    if(a >= 0)
    {
        tx_min = (m_x0 - ox) * a;
        tx_max = (m_x1 - ox) * a;
    }
    else
    {
        tx_min = (m_x1 - ox) * a;
        tx_max = (m_x0 - ox) * a;
    }

    float b = 1.0f / (dy != 0 ? dy : 1.0f);
    if(b >= 0)
    {
        ty_min = (m_y0 - oy) * b;
        ty_max = (m_y1 - oy) * b;
    }
    else
    {
        ty_min = (m_y1 - oy) * b;
        ty_max = (m_y0 - oy) * b;
    }

    float c = 1.0f / (dz != 0 ? dz : 1.0f);
    if(c >= 0)
    {
        tz_min = (m_z0 - oz) * c;
        tz_max = (m_z1 - oz) * c;
    }
    else
    {
        tz_min = (m_z1 - oz) * c;
        tz_max = (m_z0 - oz) * c;
    }

    float t0, t1;
    int face_in, face_out;

    //largest entering T Value
    if(tx_min > ty_min)
    {
        t0 = tx_min;
        face_in = (a >= 0.0f) ? 0 : 3;
    }
    else
    {
        t0 = ty_min;
        face_in = (b >= 0.0f) ? 1 : 4;
    }

    if(tz_min > t0)
    {
        t0 = tz_min;
        face_in = (c >= 0.0) ? 2 : 5;
    }

    //find smallest extiting T val
    if(tx_max < ty_max)
    {
        t1 = tx_max; 
        face_out = (a >= 0.0f) ? 3 : 0;
    }
    else
    {
        t1 = ty_max;
        face_out = (b >= 0.0f) ? 4 : 1;
    }

    if(tz_max < t1)
    {
        t1 = tz_max;
        face_out = (c >= 0.0f) ? 5 : 2;
    }

    //this is the condition for a hit
    if(t0 < t1 && t1 > 0.001f)
    {
        if(t0 > 0.001f)
        {
            tmin = t0;
            data.m_hitNormal = GetNormal(face_in);
        }
        else
        {
            tmin = t1;
            data.m_hitNormal = GetNormal(face_out);
        }
        ray.m_depth = tmin; 
        data.m_hitPoint = ray.m_origin + (ray.m_dir*tmin);
        data.m_hitTarget = (Renderable*)this;
        return true;
    }
    return false;
}

Vec3 AABBox::GetNormal( int face_in ) const
{
    switch(face_in)
    {
        case 0: return Vec3(-1,0,0); // -x face
        case 1: return Vec3(0,-1,0); // -y face
        case 2: return Vec3(0,0,-1); // -z face
        case 3: return Vec3( 1,0,0); // +x face
        case 4: return Vec3( 0,1,0); // +y face
        case 5: return Vec3( 0,0,1); // +z face
    }
    assert(0);
    return Vec3();
}
